//
// Created by Pulsar on 2019/7/18.
//

#include <Monitor64x32.h>

void Monitor64x32::link_cpu(CHIP8 &cip8) {
    this->cpu = cip8;
}

Monitor64x32::Monitor64x32(QWidget *parent) : QMainWindow(parent) {
    QTimer *timer = new QTimer(this); //this 为parent类, 表示当前窗口
    timer->start(10);


    glwindow = new Monitor64x32GL(this);
    setCentralWidget(glwindow);
    glwindow->setMinimumSize(640, 320);
    this->setMaximumSize(640, 320);

    connect(timer, SIGNAL(timeout()), this, SLOT(timeout_sc()));
    connect(glwindow, SIGNAL(paintGL()), this, SLOT(paint_screen()));
    connect(this, SIGNAL(repaint_screen()), glwindow, SLOT(repaint()));
    connect(this, SIGNAL(draw_pixel(int, int, float)), glwindow,
            SLOT(paint_pixel(int, int, float))); // SLOT填入一个槽函数
}

void Monitor64x32::paint_cell(int row, int col, unsigned char color) {
    int pixel_row = row * PIXEL_SIZE;
    int pixel_col = col * PIXEL_SIZE;
    int drow, dcol;

    for (drow = 0; drow < PIXEL_SIZE; drow++) {
        for (dcol = 0; dcol < PIXEL_SIZE; dcol++) {
            paint_pixel(pixel_row + drow, pixel_col + dcol, color);
        }
    }
}

void Monitor64x32::paint_pixel(int row, int col, unsigned char color) {
    row = cpu.screen_rows - 1 - row;
    cpu.screen[row][col][0] = cpu.screen[row][col][1] = cpu.screen[row][col][2] = color;
}

void Monitor64x32::paint_screen() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
//    glTranslatef(-1.0, 1.0, 0.0);
    for (int row = 0; row < GFX_ROWS; row++) {
        for (int col = 0; col < GFX_COLS; col++) {
            paint_cell(row, col, cpu.gfx[row][col] ? WHITE : BLACK);
        }
    }

    glDrawPixels(SCREEN_COLS, SCREEN_ROWS, GL_RGB, GL_UNSIGNED_BYTE,
                 (void *) cpu.screen);
    glPixelZoom(2, 2);
}

void Monitor64x32::keyPressEvent(QKeyEvent *event) {
    unsigned char k = event->key();
    int index = keymap(k);
    if (index >= 0) cpu.setkeys(index, 1);
}

int Monitor64x32::keymap(unsigned char k) {
    switch (k) {
        case '1':
            return 0x1;
        case '2':
            return 0x2;
        case '3':
            return 0x3;
        case '4':
            return 0xc;

        case 'Q':
            return 0x4;
        case 'W':
            return 0x5;
        case 'E':
            return 0x6;
        case 'R':
            return 0xd;

        case 'A':
            return 0x7;
        case 'S':
            return 0x8;
        case 'D':
            return 0x9;
        case 'F':
            return 0xe;

        case 'Z':
            return 0xa;
        case 'X':
            return 0x0;
        case 'C':
            return 0xb;
        case 'V':
            return 0xf;

        default:
            return -1;
    }
}

void Monitor64x32::keyReleaseEvent(QKeyEvent *event) {
    unsigned char k = event->key();

    int index = keymap(k);
    if (index >= 0) cpu.setkeys(index, 0);
}


Monitor64x32::~Monitor64x32() {
    delete glwindow;
}

void Monitor64x32::timeout_sc() {
    struct timeval clock_now;
    gettimeofday(&clock_now, NULL);

    cpu.runCycle();

    if (cpu.draw_flag) {
        repaint_screen();
        cpu.draw_flag = false;
    }
    if (timediff_ms(&clock_now, &clock_prev) >= cpu.clock_rate_ms) {
        cpu.tick();
        clock_prev = clock_now;
    }
};

int Monitor64x32::timediff_ms(struct timeval *end, struct timeval *start) {
    int diff = (end->tv_sec - start->tv_sec) * 1000 +
               (end->tv_usec - start->tv_usec) / 1000;
    //printf("timediff = %d\n", diff);
    return diff;
}


